import http from 'src/utils/request'
import { Method } from 'axios'

type Result<Data> = {
    success: boolean
    data: Data
    message: string
}

export function extractData<Data = any>(result: Result<Data>): Promise<Data> {
    if (result.success) {
        return Promise.resolve(result.data)
    } else {
        return Promise.reject(new Error(result.message))
    }
}

export function extractDataFromAny<Data = any>(result: any): Promise<Data> {
    return extractData(result)
}

export class Loader<Data = any> {
    readonly api: string
    readonly method: Method
    baseParams: any = {}
    dynamicParams?: () => object
    get data(): Data | undefined { return this._data }
    get isLoading(): boolean { return this.loadingCount > 0 }
    get error(): Error | undefined { return this._error }

    protected _data: Data | undefined = undefined
    protected _error: Error | undefined = undefined
    private _requestId: number = 0
    private abortController: AbortController| undefined = undefined
    private loadingCount = 0

    apiAdapter: (data:Data) => Promise<Data> = (data) => Promise.resolve(data)

    constructor(api: string, method: Method, dynamicParams?: () => object) {
        this.api = api
        this.method = method
        this.dynamicParams = dynamicParams
    }

    async load(additionalParams?: any): Promise<Data> {
        this.abortController?.abort()
        const abortCtrl = new AbortController()
        this.abortController = abortCtrl
        const requestId = this._requestId + 1
        this._requestId = requestId;
        const _params = {
            ...this.baseParams,
            ...this.dynamicParams?.(),
            ...additionalParams
        }
        this.loadingCount += 1
        this._error = undefined
        let request: Promise<Data> = http(this.api, {
            method: this.method,
            data: _params,
            signal: abortCtrl.signal
        }).then(extractDataFromAny)
            .then(this.apiAdapter)
        return new Promise<Data>(async (resolve, reject) => {
            try {
                const data = await request
                if (this._requestId !== requestId) {
                    console.log('已丢弃过期的请求数据',data)
                    return //中断已过期请求的promise
                }
                this._data = data
                resolve(data)
            } catch (error: any) {
                if (error.isAbort) {
                    console.log('已忽略取消的请求',error)
                    return  //中断已取消请求的promise
                }
                this._error = error
                reject(error)
            } finally {
                this.loadingCount -= 1
            }
        })
    }
}